package db

import (
	"LunovaDB/errors"
	"LunovaDB/util"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"path/filepath"
	"sync"
	"syscall"
	"time"
)

var maxFileId uint32 = 0

// keydir 内存中的索引结构
type keydir struct {
	fileId    uint32
	index     int64
	valueSize uint32
}

type LunovaDB struct {
	indexes     map[string]*keydir
	hintFile    *DBFile
	activeFile  *DBFile
	dirPath     string
	hintDirPath string
	mu          sync.RWMutex
	DBFiles     map[uint32]*DBFile

	LRUCache    util.LRUCache
	MergeHeader func(db *LunovaDB)
}

// Config 配置文件结构
type Config struct {
	CurIP       string `json:"curIP"`
	CurPort     string `json:"curPort"`
	MaxLRULen   int    `json:"maxLRULen"`
	MaxFileSize int    `json:"maxFileSize"` //单位GB
}

var config Config

// ConfigInit 初始化配置文件结构
func ConfigInit() {
	config.CurIP = ""
	config.CurPort = "8081"
	config.MaxLRULen = 0   //默认为0，不在内存中存储val
	config.MaxFileSize = 2 //默认每个文件的存储阈值为2GB
}

// Open 打开数据库文件，并初始化索引结构
// 传入参数反别为hint文件目录和db文件目录
func Open(hintdirPath, dbdirPath string) (*LunovaDB, error) {
	dirAbsPath, err := filepath.Abs(dbdirPath)
	hintDirAbsPath, err := filepath.Abs(hintdirPath)
	hintFile, err := ReadHintFile(hintdirPath)
	if err != nil {
		return nil, err
	}
	dbFiles, err := ReadAllDBFile(dbdirPath)
	if err != nil {
		return nil, err
	}
	file, err := os.Open(fmt.Sprintf("%s/config.json", "./config"))
	if err != nil {
		os.MkdirAll("./config", 0755)
		file, _ = os.Create(fmt.Sprintf("%s/config.json", "./config"))
		ConfigInit()
		marshal, _ := json.Marshal(config)
		file.Write(marshal)
	}
	defer func(file *os.File) {
		err := file.Close()
		if err != nil {
			log.Println(err)
		}
	}(file)
	err = json.NewDecoder(file).Decode(&config)
	if err != nil {
		ConfigInit()
	}
	db := &LunovaDB{
		hintFile:    hintFile, //hintfile
		DBFiles:     dbFiles,
		dirPath:     dirAbsPath,
		hintDirPath: hintDirAbsPath,
		indexes:     make(map[string]*keydir),
		LRUCache:    util.Constructor(config.MaxLRULen),
		MergeHeader: DefaultMerge,
	}
	db.loadIndexesFromFile()
	if maxFileId != 0 {
		if db.DBFiles[maxFileId].Offset < int64(config.MaxFileSize*1024*1024*1024) {
			db.activeFile = db.DBFiles[maxFileId]
		} else {
			db.activeFile = nil
		}
	} else {
		db.activeFile = nil
	}
	return db, nil
}

// ReadHintFile 获取hint文件的文件指针
func ReadHintFile(dirPath string) (*DBFile, error) {
	if _, err := os.Stat(dirPath); os.IsNotExist(err) {
		if err := os.MkdirAll(dirPath, 0755); err != nil {
			return nil, err
		}
	}
	dirAbsPath, err := filepath.Abs(dirPath)
	if err != nil {
		return nil, err
	}
	hintFile, err := NewHintFile(dirAbsPath)
	return hintFile, nil
}

// ReadAllDBFile 获取所有db数据文件的文件指针
func ReadAllDBFile(dirPath string) (map[uint32]*DBFile, error) {
	dbFiles := make(map[uint32]*DBFile)
	if _, err := os.Stat(dirPath); os.IsNotExist(err) {
		if err := os.MkdirAll(dirPath, 0755); err != nil {
			return nil, err
		}
	}
	dirAbsPath, err := filepath.Abs(dirPath)
	if err != nil {
		return nil, err
	}
	files, err := os.ReadDir(dirPath)
	for _, file := range files {
		dbfile, err := NewDBFile(dirAbsPath, file.Name())
		if err != nil {
			return nil, err
		}
		dbFiles[dbfile.Id] = dbfile
	}
	return dbFiles, nil
}

// 从hint文件当中加载索引
func (db *LunovaDB) loadIndexesFromFile() {
	if db.hintFile == nil {
		return
	}

	var offset int64
	for {
		e, err := db.hintFile.HintRead(offset)
		if err != nil {
			// 读取完毕
			if err == io.EOF {
				// fmt.Println("offset", offset)
				break
			}
			return
		}
		mem := keydir{
			index:     int64(e.ValuePos),
			fileId:    e.FileId,
			valueSize: e.ValueSize,
		}
		db.indexes[string(e.Key)] = &mem
		// fmt.Println("key", string(e.Key))
		// fmt.Println("fileIndex", db.indexes[string(e.Key)].index)
		// fmt.Println("fileId", db.indexes[string(e.Key)].fileId)
		// fmt.Println("maxFileId", maxFileId)
		offset += e.GetSize()
	}
}

// Merge 合并数据文件
// 现在只做了写入hint操作
func (db *LunovaDB) Merge() error {
	// 没有数据，忽略
	if len(db.DBFiles) == 0 {
		return nil
	}

	var (
		validEntries []*Entry
		offset       int64
	)
	indexMap := make(map[*Entry]*keydir)
	// 读取原数据文件中的 Entry
	// e, err := db.dbFile.Read(offset)
	// if err != nil {
	// 	if err == io.EOF {
	// 		break
	// 	}
	// 	return err
	// }
	// // 内存中的索引状态是最新的，直接对比过滤出有效的 Entry
	// //off := db.indexes.RbTreeGet(string(e.Key))
	// //if off != nil && off.(int64) == offset {
	// //	validEntries = append(validEntries, e)
	// //}
	// if off, ok := db.indexes[string(e.Key)]; ok && off == offset {
	// 	validEntries = append(validEntries, e)
	// }
	// offset += e.GetSize()
	var i uint32 = 1
	for ; i <= maxFileId; i++ {
		offset = 0
		if _, ok := db.DBFiles[i]; ok {
			for {

				e, err := db.DBFiles[i].Read(offset)
				if err != nil {
					if err == io.EOF {
						break
					}
					return err
				}
				// 内存中的索引状态是最新的，直接对比过滤出有效的 Entry
				//off := db.indexes.RbTreeGet(string(e.Key))
				//if off != nil && off.(int64) == offset {
				//	validEntries = append(validEntries, e)
				//}
				fmt.Println("e.Key", string(e.Key), "offset", offset)
				if off, ok := db.indexes[string(e.Key)]; ok && off.index == offset {
					validEntries = append(validEntries, e)
					indexMap[e] = off
				}
				offset += e.GetSize()
			}
		}
	}
	//for _, i := range validEntries {
	//	fmt.Println("validEntries", string(i.Key))
	//}
	//for i, v := range db.indexes {
	//	fmt.Println(i, v)
	//}
	// 新建临时文件
	// mergeDBFile, err := NewMergeDBFile(db.dirPath)
	// if err != nil {
	// 	return err
	// }
	// defer func() {
	// 	_ = os.Remove(mergeDBFile.File.Name())
	// }()

	db.mu.Lock()
	defer db.mu.Unlock()
	//将索引写入hint文件中

	// 获取文件名
	hintFileName := db.hintFile.File.Name()
	// 关闭文件
	_ = db.hintFile.File.Close()
	// 删除旧的数据文件
	_ = os.Remove(hintFileName)
	// _ = mergeDBFile.File.Close()
	// 获取文件名
	// mergeDBFileName := mergeDBFile.File.Name()
	// 临时文件变更为新的数据文件

	newHintFile, err := NewHintFile(db.hintDirPath)

	// _ = os.Rename(mergeDBFileName, filepath.Join(db.dirPath, FileName))

	// dbFile, err := NewDBFile(db.dirPath)
	if err != nil {
		return err
	}
	db.hintFile = newHintFile
	for _, entry := range validEntries {
		// fmt.Println("entryID", indexmap[entry].fileId)
		index := uint64(indexMap[entry].index)
		// fmt.Println("uint64index", index)
		hintEntry := NewHintEntry(entry.Key, entry.KeySize, entry.ValueSize, entry.Timestamp, index, indexMap[entry].fileId)
		err := db.hintFile.HintWrite(hintEntry)
		if err != nil {
			return err
		}
	}

	// // 重新写入有效的 entry
	// for _, entry := range validEntries {
	// 	writeOff := mergeDBFile.Offset
	// 	err = mergeDBFile.Write(entry)
	// 	if err != nil {
	// 		return err
	// 	}

	// 	// 更新索引
	// 	//db.indexes.RbTreeMod(string(entry.Key), writeOff)
	// 	db.indexes[string(entry.Key)] = writeOff
	// }

	// // 获取文件名
	// dbFileName := db.dbFile.File.Name()
	// // 关闭文件
	// _ = db.dbFile.File.Close()
	// // 删除旧的数据文件
	// _ = os.Remove(dbFileName)
	// _ = mergeDBFile.File.Close()
	// // 获取文件名
	// mergeDBFileName := mergeDBFile.File.Name()
	// // 临时文件变更为新的数据文件
	// _ = os.Rename(mergeDBFileName, filepath.Join(db.dirPath, FileName))

	// dbFile, err := NewDBFile(db.dirPath)
	// if err != nil {
	// 	return err
	// }

	// db.dbFile = dbFile
	return nil
}

// Put 写入数据
func (db *LunovaDB) Put(key []byte, value []byte) (err error) {
	if len(key) == 0 {
		return
	}
	db.mu.Lock()
	defer db.mu.Unlock()
	if db.activeFile == nil {
		err := db.CreatActiveFile()
		if err != nil {
			return err
		}
	}
	// 判断 key 是否存在
	_, err = db.exist(key)
	if err == nil {
		return errors.ErrKeyAlreadyExists
	}
	offset := db.activeFile.Offset
	// 封装成 Entry

	entry := NewEntry(key, value, PUT, uint32(time.Now().Unix()))
	// 追加到数据文件当中
	err = db.activeFile.Write(entry)
	// 写到内存
	mem := keydir{
		fileId:    db.activeFile.Id,
		index:     offset,
		valueSize: uint32(len(value)),
	}
	db.indexes[string(key)] = &mem
	if config.MaxLRULen > 0 {
		db.LRUCache.Put(string(key), string(value))
	}

	if db.activeFile.Offset > int64(config.MaxFileSize*1024*1024*1024) {
		db.activeFile = nil
	}
	// if db.activeFile.Offset > 0 {
	// 	db.activeFile = nil
	// 	fmt.Println("第一次", maxFileId)
	// }
	return
}

// Mod 修改数据
// 与put的区别在于，mod会覆盖原有数据，而put则会返回error
func (db *LunovaDB) Mod(key []byte, value []byte) (err error) {
	if len(key) == 0 {
		return
	}

	db.mu.Lock()
	defer db.mu.Unlock()
	if db.activeFile == nil {
		err := db.CreatActiveFile()
		if err != nil {
			return err
		}
	}
	offset := db.activeFile.Offset
	// 封装成 Entry
	entry := NewEntry(key, value, PUT, uint32(time.Now().Unix()))
	// 追加到数据文件当中
	err = db.activeFile.Write(entry)

	// 写到内存
	//db.indexes.RbTreeMod(string(key), offset)
	mem := &keydir{
		fileId:    db.activeFile.Id,
		index:     offset,
		valueSize: uint32(len(value)),
	}
	db.indexes[string(key)] = mem
	if config.MaxLRULen > 0 {
		db.LRUCache.Put(string(key), string(value))
	}
	if db.activeFile.Offset > int64(config.MaxFileSize*1024*1024*1024) {
		db.activeFile = nil
	}
	return
}

// exist key值是否存在与数据库
// 若存在返回对应的内存结构；不存在返回ErrKeyNotFound
func (db *LunovaDB) exist(key []byte) (*keydir, error) {
	// 从内存当中取出索引信息

	//offset := db.indexes.RbTreeGet(string(key))
	//// key 不存在
	//if offset == nil {
	//	return 0, errors.ErrKeyNotFound
	//}
	//return offset.(int64), nil
	mem, ok := db.indexes[string(key)]
	if !ok {
		return nil, errors.ErrKeyNotFound
	}
	return mem, nil
}

// Get 取出数据
func (db *LunovaDB) Get(key []byte) (val []byte, err error) {

	if len(key) == 0 {
		return
	}

	db.mu.RLock()
	defer db.mu.RUnlock()

	value := db.LRUCache.Get(string(key))
	if value != "" {
		val = []byte(value)
		return
	}

	mem, err := db.exist(key)
	if err == errors.ErrKeyNotFound {
		return
	}
	// fmt.Println("memId", mem.fileId)
	// 从磁盘中读取数据
	var e *Entry
	e, err = db.DBFiles[mem.fileId].Read(mem.index)
	if err != nil && err != io.EOF {
		return
	}
	if e != nil {
		val = e.Value
	}
	return
}

// GetTime 获取插入key时对应的时间戳
func (db *LunovaDB) GetTime(key []byte) (val uint32, err error) {
	if len(key) == 0 {
		return
	}

	db.mu.RLock()
	defer db.mu.RUnlock()

	mem, err := db.exist(key)
	if err == errors.ErrKeyNotFound {
		return
	}

	// 从磁盘中读取数据
	var e *Entry
	e, err = db.DBFiles[mem.fileId].Read(mem.index)
	if err != nil && err != io.EOF {
		return
	}
	if e != nil {
		val = e.Timestamp
	}
	return
}

// Del 删除数据
func (db *LunovaDB) Del(key []byte) (err error) {
	if len(key) == 0 {
		return
	}
	db.mu.Lock()
	defer db.mu.Unlock()

	if db.activeFile == nil {
		err := db.CreatActiveFile()
		if err != nil {
			return err
		}
	}
	_, err = db.exist(key)
	if err == errors.ErrKeyNotFound {
		err = nil
		return
	}

	// 封装成 Entry 并写入
	e := NewEntry(key, nil, DEL, uint32(time.Now().Unix()))
	err = db.activeFile.Write(e)
	if err != nil {
		return
	}

	// 删除内存中的 key
	//db.indexes.RbTreeDel(string(key))
	delete(db.indexes, string(key))
	if config.MaxLRULen > 0 {
		db.LRUCache.Delete(string(key))
	}
	if db.activeFile.Offset > int64(config.MaxFileSize*1024*1024*1024) {
		db.activeFile = nil
	}
	return
}

// Close 关闭 db 实例
func (db *LunovaDB) Close() error {
	if db.DBFiles == nil || db.hintFile == nil {
		return errors.ErrInvalidDBFile
	}
	//关闭前进行Merge
	err := db.Merge()
	if err != nil {
		return err
	}
	db.hintFile.File.Close()
	for _, file := range db.DBFiles {
		file.File.Close()
	}
	return nil
}

// NetCon 是否开启网络连接
func (db *LunovaDB) NetCon() {
	//默认Merge函数
	go db.MergeHeader(db)
	//检测系统终端信号
	go func() {
		sigChan := make(chan os.Signal, 1)
		signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
		<-sigChan
		fmt.Println("Received Ctrl+C, exiting gracefully...")
		// 执行清理逻辑
		db.Close()
		// 程序会正常退出，defer 会执行
		os.Exit(0)
	}()
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%s", config.CurIP, config.CurPort))
	if err != nil {
		fmt.Println("Error listening:", err.Error())
		return
	}
	defer listener.Close()
	fmt.Println("Listening on port ", config.CurPort)
	for {
		conn, err := listener.Accept()
		conns := util.ConnList{
			Net:     conn,
			Wbuffer: make([]byte, 0),
			Rbuffer: make([]byte, 0),
			Wlen:    0,
			Rlen:    0,
			DB:      db,
		}
		if err != nil {
			fmt.Println("Error accepting:", err.Error())
			continue
		}
		go util.HandleConnection(&conns)
	}
}

// DefaultMerge 默认Merge函数 每小时执行一次merge操作
func DefaultMerge(db *LunovaDB) {
	for {
		err := db.Merge()
		if err != nil {
			log.Fatal(err)
		}
		time.Sleep(1 * time.Hour)
	}
}

// CreatActiveFile 创建活跃文件,并加入到DBFiles中
func (db *LunovaDB) CreatActiveFile() error {
	act, err := NewDBFile(db.dirPath, fmt.Sprintf(FileName, maxFileId+1))
	if err != nil {
		return errors.CreateActiveFileError
	}
	db.activeFile = act
	db.DBFiles[act.Id] = act
	return nil
}

func (db *LunovaDB) LoadDBFile(filePath string) error {
	dbFile, err := newInternal(filePath, entryHeaderSize)
	defer dbFile.File.Close()
	if err != nil {
		return err
	}
	var offset int64 = 0
	for {
		e, err := dbFile.Read(offset)
		if err != nil {
			// 读取完毕
			if err == io.EOF {
				// fmt.Println("offset", offset)
				break
			}
			return err
		}
		db.Put(e.Key, e.Value)
		if _, err := db.exist(e.Key); err != nil {

			mem := keydir{
				index:     db.activeFile.Offset - e.GetSize(),
				fileId:    db.activeFile.Id,
				valueSize: e.ValueSize,
			}
			db.indexes[string(e.Key)] = &mem

		}
		offset += e.GetSize()
	}
	return nil
}
